- 引用类型的值(对象)是引用类型的一个实例。
- 引用类型是一种数据结构,用于将数据和功能组织在一起。它同行被称为类,但这种称呼并不妥当,尽管ECMAScript从技术上讲是一门面向对象的语言,但他不具备传统的面向对象语言所支持的接口等基本结构。
- 引用类型有时候也被称为对象定义
-
关于typeof中的"object"和Object()构造函数的个人理解:
- typeof操作符返回的"object"值指的是数据类型
- 在new Object()中"Object"表示的是数据结构(引用类型),所以new Object()返回的是基于Object引用类型的一个实例
- typeof返回的"function" 和 Function()构造函数 同理
Object类型
- 创建对象的方法一个new Object(),另一个是对象字面量表示法
- 字面量表示法的最后一个属性不能添加逗号,这样会在IE7以前和Opera中导致错误
- 属性名会自动转换为字符串
- 属性名中包含关键字、保留字会报错
- 通常,除非必须使用变量来访问属性,否则我们建议使用点表示法
// 这里所有的属性名都会自动转为字符串
var person = {
"name": "Nicholas",
age: 29,
5: true
}
// 不会报错,但这个属性不能使用person.name的方式
person["first name"]
Array类型
- 创建数组的基本方式有两种。使用Array构造函数,字面量表示法
var colors = new Array();
var colors = new Array(20); // 传入一个数值 创建一个length为20的空数组
var colors = new Array("red"); // 传入非数值 因此为元素的数组
var colors = new Array("red", "blue", "green");
var colors = ["red", "blue", "green"];
colors.length = 2;
alert(colors[2]); // undefined
var colors = ["red", "blue", "green"];
colors[colors.length] = "black";
colors[colors.length] = "brown"; // 添加新项
var colors = ["red", "blue", "green"];
colors[99] = "black";
alert(colors.length); // 100 位置3到98的值都不存在,都将返回undefined
检测数组
- 对于一个网页或者一个全局作用域而言,使用instanceof操作符就能得到结果
if (value instanceof Array) {
...
}
- 如果网页中包含多个 框架,实际上就存在两个以上的不同全局执行环境,从而存在两个以上不同版本的Array构造函数,为此ECMAScript5新增了Array.isArray()方法.IE9+,Firefox4+,safari5+,Opera10.5+ Chrome
if (Array.isArray(value)) {
...
}
转换方法
- 所有对象都据欧toLocaleString(),toString(),valueOf()方法。toString()返回由逗号拼接的字符串,valueOf()返回的还是数组
- join() 如果传入undefined,默认逗号分隔,IE7以前的版本会错误的以undefined分隔
- 如果数组中某一项是null或者undefined,那么该值在join(),toLocaleString(),toString(),valueOf()方法返回的结果以空字符串表示
栈方法
- 栈是一种LIFO(Last-In-First-Out 后进先出)的数据结构。而栈中的插入(推入)和移除(弹出),只发生在一个位置——栈的顶部。ECMAScript为数组专门提供了push()和pop()方法
var colors = ["red", "blue", "green"];
var item = colors.pop();
alert(colors.length); // 2
alert(item) // “black”取得最后一项
队列方法
- 队列数据结构的访问规则是FIFO(First-In-First-Out 先进先出)
- shift()方法能移出数组的第一项并返回该项,结合使用shift()和push()方法可以实现
- unshift()方法能在数组前端添加任意个项并返回新数组的长度, IE7以下的版本总是返回undefined,IE8在非兼容模式下会返回正确的长度值
var colors = ["red", "blue", "green"];
colors.push("black");
var item = colors.shift();
console.log(colors); // ["blue", "green", "black"]
console.log(item) // “red”取得最后一项
var colors = new Array();
colors.unshift("red", "green"); // 推入两项
console.log(colors.length); // 2
重排序方法
- 数组存在两个直接重排序的方法 reverse() sort()
- reverse()直接返回将原数组倒序排列
- sort() 调用每项的toString()转型方法,按照升序排列,即使数组中的每一项都是数值,sort()方法比较的也是字符串
- sort()可传入一个比较函数,比较函数接受两个参数,如果第一个参数应该位于第二个之前
var values = [0, 1, 5, 10, 15];
values.sort();
console.log(values); // 0,1,10,15,5
// 使用比较函数是sort的最佳使用方式
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
var values = [0, 1, 10, 15, 5];
values.sort(compare);
console.log(values); // 0,1,5,10,15
// 对于数值类型或者其valueOf()方法会返回数值类型的对象类型
// 可以使用一个更简单的比较函数
function compare(value1, value2) {
return value2 - value1
}
操作方法
- concat() 先创建一个数组副本,再将传入的元素添加到新数组的末尾
var colors = ["red", "green", "blue"];
var colors2 = colors.concat("yellow", ["black", "brown"]);
alert(colors); // red,green,blue
alert(colors2); // red,green,blue,yellow,black,brown
- slice()可以接收一个或两个参数,基于当前数组创建新数组,返回指定位置之间的所有项,slice()方法不会影响原数组
- 如果slice参数有一个负数,则用数组长度加上该数来确定相应的位置
var colors = ["red", "green", "blue", "yellow", "purple"];
var colors2 = colors.slice(1); // green,blue,yellow,purple
var colors3 = colors.slice(1,4); // green,blue,yellow
-
splice() 最强大的数组方法,始终返回一个数组,该数组中包含从原始数组中删除的项(没有就返回空数组)
- 删除:可以删除任意数量的项,只需指定2个参数:要删除的第一项的位置和要删除的项数
- 插入:可以向指定位置插入任意数量的项,只需提供3个参数:起始位置、0(插入操作没有要删除的项所以是0)和要插入的项,多个可以传任意个数的参数
- 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项,删除的项数不必与插入的项数相等
var colors = ["red", "green", "blue"];
var removed = colors.splice(0, 1); // 删除第一项 removed 为 ["red"]
alert(colors); // ["green", "blue"]
removed = colors.splice(1, 0, "yellow", "orange"); // 从位置1开始插入两项
console.log(colors); // green,yellow,orange,blue
console.log(removed); // 返回空数组
removed = colors.splice(1, 1, "red", "purple"); // 插入两项,删除一项
console.log(colors); // green,red,purple,orange,blue
console.log(removed); // ["yellow"]
位置方法
- 数组有两个位置方法,都接收两个参数:要查找的项和(可选)表示查找气垫位置的索引,返回查找对应项的位置,没有找到返回-1。在比较第一个参数与数组中的每一项时,会使用全等操作符;
- indexOf() 从开头(位置0)查找
- lastIndexOf() 从末尾开始向前查找
- 支持的浏览器 IE9+, Firefox2+, safari3+, Opera9.5+ Chrome
var numbers = [1,2,3,4,5,4,3,2,1];
alert(numbers.indexOf(4)); // 3
alert(numbers.lastIndexOf(4)); // 5
alert(numbers.indexOf(4, 4)); // 5
alert(numbers.lastIndexOf(4, 4)); // 3
var person = { name: "Nicholas" };
var people = [{ name: "Nicholas" }];
var morePeople = [people];
alert(people.indexOf(person)); // -1
alert(morePeople.indexOf(person)); // 0
// 两个引用类型对象值完全相同,但引用的内存地址不同
var a = { name: "obj" }
var b = { name: "obj" }
a == b // false
a === b // false
迭代方法
- ECMAScript5位数组定义了5个迭代方法,每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值,该项在数组中的位置和数组对象本身。
- every():对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true
- filter():对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组
- forEach():对数组中的每一项运行给定函数。这个方法没有返回值
- map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
- some():对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。
- 以上方法都不会修改数组中的包含的值
- 支持的浏览器 IE9+, Firefox2+, safari3+, Opera9.5+ Chrome
归并方法
- ECMAScript5新增了两个归并数组的方法 reduce()和reduceRight()。迭代数组的所有项,然后构建一个最终返回的值
- reduce() 从数组的第一项开始,逐个遍历到最后
- reduceRight() 则从最后一项开始
- 支持的浏览器 IE9+, Firefox3+, safari4+, Opera10.5+ Chrome
var values = [1,2,3,4,5];
// prev 前一项, cur 迭代的当前项
// 第一次执行回调函数,prev是1,cur是2
var sum = values.reduce(function(prev, cur, index, array) {
return prev + cur;
});
alert(sum); // 15
Date类型
- 下面两行代码是等价的,Date构造函数会在后台调用Date.parse()
- 不用Date.parse()或Date.UTC() 都是基于系统设置本地时区创建的
- Date.now() 返回调用时日期和时间的毫秒数,支持的浏览器IE9+, Firefox3+, safari3+, Opera10.5+ Chrome
var someDate = new Date(Date.parse("May 25, 2004"));
var someDate = new Date("May 25, 2004");
// GMT时间200年1月1日午夜零时
var y2k = new Date(Date.UTC(2000, 0));
var y2k = new Date(2000, 0);
// GMT时间2005年5月5日下午5:55:55
var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));
var allFives = new Date(005, 4, 5, 17, 55, 55);
// Date.now()在不支持的浏览器中如下写法
var start = +new Date();
start // 返回毫秒数
typeof start // "number"
继承方法
- toLocaleString()和toSring()的这一差别仅在调试代码时比较有用
- valueOf()方法,不返回字符串,返回日期的毫秒表示
var date1 = new Date(2007, 0, 1); // "January 1 , 2007"
var date2 = new Date(2007, 1, 1); // "February 1, 2007"
console.log(date1 < date2); // true
console.log(date1 > date2); // false
日期格式化方法
- toDateString() 特定格式显示星期几、月、日和年
- toTimeString() 特定格式显示 时、分、秒和时区
- toLocaleDateString() 特定于地区格式显示星期几、月、日和年
- toLocaleTimeString() 特定于地区实现的格式显示时、分、秒
- toUTCString() 特定于实现的格式UTC日期
日期/时间组件方法
- 图表略...
RegExp类型
- ECMAScript通过regExp类型来支持正则表达式
- 每个正则表达式都可带有一或多个标志(flags)
- g:表示全局模式(global),即模式将被应用于所有字符串,而非在发现第一个匹配项时立即停止
- i:表示不区分大小写(case-insensitive)模式,即在确定匹配项时忽略模式与字符串的大小写
- m:表示多行模式(multiline),即在到达一行文本末尾时还会继续查找下一行中是否存在于模式匹配的项
// 匹配字符串中所有“at”的实例
var pattern1 = /at/g;
// 匹配第一个"bat"或"cat",不区分大小写
var pattern2 = /[bc]at/i;
// 匹配所有以"at"结尾的3个字符的组合,不区分大小写
var pattern3 = /.at/gi;
- 模式中使用的所有元字符都必须转义:( [ { ^ $ | ) ? * + . ] }
- 使用RegExp构造函数,两个参数都必须是字符串,所有元字符都要双重转义
// 匹配第一个"bat"或"cat", 不区分大小写
var patter1 = /[bc]at/i;
// 匹配第一个"[bc]at", 不区分大小写
var pattern2 = /\[bc\]at/i;
// 匹配所有以"at"结尾的3个字符的组合,不区分大小写
var pattern3 = /.at/gi
// 匹配所有".at",不区分大小写
var pattern4 = /\.at/gi;
// 使用RegExp构造函数,所有元字符都要双重转义
// 两个参数都必须是字符串
var pattern4 = new RegExp("\\.at", "i");
- 我们可以使用小括号"()"来指定要重复的子表达式,然后对这个子表达式进行重复,例如:(abc)? 表示0个或1个abc 这里一 个括号的表达式就表示一个分组 。分组可以分为两种形式,捕获组和非捕获组。
-
捕获组可以通过从左到右计算其开括号来编号 。例如,在表达式 (A)(B(C)) 中,存在四个这样的组。组零始终代表整个表达式。之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。在表达式中使用,也可以在匹配操作完成后从匹配器检索。
编号 项目 0 (A)(B(C)) 1 (A) 2 (B(C)) 3 (C) - 以 (?) 开头的组是纯的非捕获组(?:Pattern),它不捕获文本 ,也不针对组合计进行计数。就是说,如果小括号中以?号开头,那么这个分组就不会捕获文本,当然也不会有组的编号。非捕获组则不会捕获文本,也不会将它匹配到的内容单独分组来放到内存中。所以,使用非捕获组较使用捕获组更节省内存。
RegExp实例属性
-
RegExp的每个实例都具有下列属性,通过这些属性可以取得有关模式的各种信息
- global 布尔值 表示是否设置了g标志
- ignoreCase 布尔值 表示是否设置了i标志
- lastIndex 整数 表示开始搜索下一个匹配项字符位置,从0算起
- multiline 布尔值 表示是否设置了m标志
- source 正则表达式的字符串表示,按照字面量形式而非传入构造函数中的字符串模式返回
var pattern1 = /\[bc\]at/i;
console.log(pattern1.global); // false
console.log(pattern1.ignoreCase); // true
console.log(pattern1.multiline); // false
console.log(pattern1.lastIndex); // 0
console.log(pattern1.source); // "\[bc\]at"
RegExp实例方法
- exec() 专门为捕获组而设计的,接收一个参数,即要应用模式的字符串,然后返回包含第一个匹配项信息的数组;或者在没有匹配项的情况下返回null
- 返回的数组虽然是Array的实例,但包含两个额外的属性:index和input
- index表示匹配项在字符串中的位置
- input表示表达式的字符串
- 即使在模式中设置了全局标志(g),exec()每次也只会返回一个匹配项,不设置g的时候,每次都返回第一个匹配项,设置了g的情况下,每次调用exec()则都会在字符串中继续查找新匹配项
var txt = "mom and dad and baby";
var pattern = /mom( and dad( and baby)?)?/gi;
var matches = pattern.exec(txt);
console.log(matches);
// ["mom and dad and baby", " and dad and baby", " and baby", index: 0, input: "mom and dad and baby", groups: undefined]
- test() 接收字符串参数,匹配返回true不匹配返回false
var txt = "000-00-0000";
var pattern = /\d{3}-\d{2}-\d{4}/;
if (pattern.test(txt)) {
alert("The pattern was matched.");
}
- toString() toLocaleString() 都会返回正则表达式的字面量,与创建表达式的方式无关
- valueOf() 返回正则表达式本身
RegExp构造函数属性
- RegExp构造函数包含一些属性,适用于作用域中的所有表达式,并且基于所执行的最近一次正则表达式操作而变化
- 这些属性分别有一个长属性名和一个短属性名(Opera例外,不支持短属性)
长属性名 | 短属性名 | 说明 |
---|---|---|
input | $_ | 最近一次要匹配的字符串。Opera不支持 |
lastMatch | $& | 最近一次的匹配项目。Opera不支持 |
lastParen | $+ | 最近一次匹配的捕获组。Opera不支持 |
leftContext | $` | input字符串中lastMatch之前的文本。 |
multiline | $* | 布尔值 表示是否所有表达式都是用多行模式。IE和Opera未实现此属性 |
rightContext | $' | input字符串中lastMatch之后的文本。 |
- 使用这些属性可以从exec() test()执行的操作中提取更具体的信息
var txt = "this has been a short summer";
var pattern = /(.)hort/g;
// Oper不支持input,lastMatch,lastParen,multiline
// IE不支持multiline
if (pattern.test(txt)) {
console.log(RegExp.input); // this has been a short summer
console.log(RegExp.leftContext); // this has been a
console.log(RegExp.rightContext); // summer
console.log(RegExp.lastMatch); // short
console.log(RegExp.lastParen); // s
console.log(RegExp.multiline); // false
}
- 短属性名打斗不是有效的ECMAScript标识符,因此必须通过方括号语法来访问
var txt = "this has been a short summer";
var pattern = /(.)hort/g;
// Oper不支持input,lastMatch,lastParen,multiline
// IE不支持multiline
if (pattern.test(txt)) {
console.log(RegExp.$_); // this has been a short summer
console.log(RegExp.["$`"]); // this has been a
console.log(RegExp.["$'"]); // summer
console.log(RegExp.["$&"]); // short
console.log(RegExp.["$+"]); // s
console.log(RegExp.["$*"]); // false
}
- 除了上面介绍的几个属性之外,还有多达9个用于存储捕获组的构造函数属性:RegExp.$1,RegExp.$2......RegExp.$9
var txt = "this has been a short summer";
var pattern = /(..)or(.)/g;
// 这里创建了一个包含两个捕获组的模式,并用该模式测试了一个字符串。
// 即使test()方法只返回一个布尔值,但RegExp构造函数的属性$1和$2也会被匹配相应捕获组的字符串自动填充
if (pattern.test(txt)) {
console.log(RegExp.$1); // sh
console.log(RegExp.$2); // t
}
模式的局限性
- 尽管ECMACScript中的正则表达式功能还是比较完备的,但仍然缺少某些语言所支持的高级正则表达式特性
-
下面列出了不支持的特性
- 匹配字符串开始和结尾A 和 Z 锚 (但支持以插入符号 ^ 和美元符号 $ 来匹配字符串的开始和结尾)
- 向后查找(lookbehind) (但完全支持向前查找lookahead)
- 并集和交集
- 原子组(atomic grouping)
- Unicode支持(耽搁字符串除外, 如 uFFFF)
- 命名的捕获组(但支持编号的捕获组)
- s(single,单行)和 x (free-spacing, 无间隔)匹配模式
- 条件匹配
- 正则表达式注释
Function类型
- 每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法
- 由于函数是对象,因此函数名实际上也是只想函数对象的指针,不会与某个函数绑定
function sum (num1, num2) {
return num1 + num2;
}
// 下面使用函数表达式定义函数的方式几乎相差无几
// function后面没必要使用函数名,通过变量sum即可饮用函数
// 函数末尾有一个分号,就像声明其他变量一样
var sum = function(num1, num2) {
return num1 + num2;
};
- 不推荐 最后一种固定翼函数的方式是使用Function构造函数,可以接收任意数量的参数,但最后一个参数始终但被看成是函数体,而前面的参数则枚举出了新函数的参数。这样会导致解析两次代码,第一次是解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串,从而影响性能。
// 不推荐
var sum = new Function("num1", "num2", "return num1 + num2");
- 函数名是指向函数的指针,因此函数名与包含对象指针的其他变量没有区别。一个函数可能会有多个名字
function sum (num1, num2) {
return num1 + num2;
}
var anotherSum = sum; // 函数指针的副本赋给了anotherSum
sum = null; // 清除了sum变量 anotherSum依然有效
console.log(anotherSum(10, 10)); // 20
没有重载(深入理解)
function addSomeNumber(num) {
return num + 100;
}
function addSomeNumber(num) {
return numm + 200;
}
var result = addSomeNumber(100); // 300
// 这个例子中声明了两个同名函数,而结果是后面的函数覆盖了前面的函数
// 以下代码等同于上面的代码
var addSomeNumber = function (num ) {
return num + 100;
};
addSomeNumber = function (num ) {
return num + 200;
};
var result = addSomeNumber(100); // 300
- 在创建第二个函数时,实际上覆盖了引用第一个函数的变量addSomeNumber
函数声明与函数表达式
- 解析器会先解读函数声明,并使其在执行任何代码之前可用
- 函数表达式,则必须等到解析器执行到它所在的代码行,才会真正被解释执行
- 也可以同时使用函数声明和函数表达式,(var sum = function sum () {};)不过在Safari中会导致错误
// 不会报错
alert(sum(10, 10));
function sum(num1, mun2) {
return num1 + num2;
}
// 报错
alert(sum(10, 10));
var sum = function (num1, mun2) {
return num1 + num2;
};
作为值的函数
- 函数名本身就是变量,所以函数也可以作为值来使用。也就是说,不仅可以像传递参数一样把一个函数传递给另一个函数,而且可以将一个函数作为另一个函数的结果返回
function callSomeFunction(someFunction, someArgument) {
return someFunction(someArgument);
}
function add10(num) {
return num + 10;
}
// 传入函数指针,就不能加(),所以这里是add10
var result1 = callSomeFunction(add10, 10);
- 可以从一个函数中返回另一个函数
function createComparisonFunction(propertyName) {
return function(object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
}
函数内部属性
- 在函数内部,有两个特殊的对象:arguments this
- arguments的主要用途保存函数参数,这个对象还有一个属性callee,该属性是一个指针,指向拥有这个arguments对象的函数
// 经典的阶乘函数
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * factorial(num-1);
}
}
// 如上面代码,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题
// 但这个函数的执行与函数名factorial紧紧耦合在了一起
// 下面的写法可以避免这种问题
function factorial(num) {
if (num <= 1) {
return 1;
} else {
return num * arguments.callee(num-1);
}
}
var trueFactorial = factorial;
fatorial = function () {
return 0;
};
console.log(trueFactorial(5)); // 120
console.log(fatorial(5)); // 0
- this 引用的是函数执行的环境对象——或者说是this 值(当在网页全局作用域中调用函数时,this对象引用的是window)
window.color = "red"
var o = { color: "blue" };
function sayColor() {
console.log(this.color);
}
sayColor(); // "red"
o.sayColor = sayColor;
o.sayColor(); // "blue"
- ECMAScript 5 规范化了另一个函数对象的属性:caller 。Opera早起版本不支持,其他浏览器都支持这个ECMAScript 3并没有定义的属性。caller保存着调用当前函数的函数引用,在全局作用用户中调用当前函数,它的值为null
function outer() {
inner();
}
function inner() {
console.log(inner.caller);
}
// 打印出outer()函数的源码
// 因为outer调用了inner,所以inner.caller就指向outer()。
outer();
// 为了实现更松散的耦合,也可以通过arguments.callee.caller来访问
function outer() {
inner();
}
function inner() {
console.log(arguments.callee.caller);
}
outer();
- 当函数再严格模式下运行时,访问arguments.callee会导致错误。ECMAScript 5 还定义了arguments.caller属性,严格模式下也会导致错误,而在非严格模式下这个属性始终是undefined。定义这个属性是为了分清arguments.caller和函数的caller属性。以上变化都是为了加强这门语言的安全性,这样第三方代码就不能再相同的环境里窥视其他代码了。
- 严格模式还有一个限制:不能为函数caller属性赋值,否则会导致错误。
函数属性和方法
-
每个函数都包含两个属性:length 和 prototype
- length表示函数希望接收的命名参数的个数
- prototype 对于引用各类型而言,prototype是保存它们所有实例方法的真正所在。换言之,注入toString() valueOf()等方法实际上都保存在prototype名下,只不过是通过各自对象的实例访问罢了。在创建自定义引用类型以及实现继承时,prototype属性的作用是极为重要的。
- 在ECMAScript 5 中,prototype属性是不可枚举的,因此使用for-in无法发现。
-
每个函数都包含两个非继承而来的方法:apply() call()。这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。
- apply()方法接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是arguments对象
- 在严格模式下,未指定对象而调用函数,则this值不会转型为window。除非明确把函数添加到某个对象或者调用apply() 或 call(),否则this值将是undefined。
- call()方法与apply()的作用相同,它们的区别仅在于接收参数的方式不同。对于call()而言,第一个参数是this值没有变化,不同的是其余参数都直接传递给函数。换言之,在使用call()方法时,传递给函数的参数必须逐个列举出来
// callSum1()在执行sum()函数时传入了this作为this值,所以传入的就是window对象
function sum(num1, num2) {
return num1 + num2;
}
function callSum1(num1, num2) {
return sum.apply(this, arguments); // 传入arguments对象
}
function callSum2(num1, num2) {
return sum.apply(this, [num1, num2]); // 传入数组
}
function callSum3(num1, num2) {
return sum.call(this, num1, num2); // 传入数组
}
// 在全局环境下调用函数,此时this就是window
callSum1(10, 10)); // 20
callSum2(10, 10)); // 20
callSum3(10, 10)); // 20
- apply() call() 真正强大的地方是能够扩充函数赖以运行的作用域。
window.color = "red";
var o = { color: "blue" };
function sayColor() {
console.log(this.color);
}
sayColor(); // red
sayColor.call(this); // red
sayColor.call(window); // red
sayColor.call(o); // blue
- 使用call() apply() 来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系
- ECMAScript 5 还定义了一个方法: bind() 这个方法会创建一个函数的实例,其this值会被绑定到传给bind()函数的值.支持bind()方法的浏览器IE9+, Firefox 4+,Safari 5.1+, Opera 12+ Chrome
window.color = "red";
var o = { color: "blue" };
function sayColor() {
console.log(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); // blue
- 每个函数继承的toLocaleString() 和 toString() valueOf() 方法都之中返回函数的代码。
基本包装类型
- 为了便于操作基本类型值,ECMAScript还提供了3个特殊的引用类型: Boolean, Number, String
- 每当读取一个基本类型值得时候,后台就会创建一个对应的基本包装类型的对象,从而让我们能够调用一些方法来操作这些数据。
// 后台自动完成下列处理
// 创建String类型的一个实例
// 在实例上调用指定的方法
// 销毁这个实例
var s1 = "some text";
var s2 = s1.substring(2);
// 可以想象执行了以下代码
var s1 = new String("some text");
var s2 = s1.substring(2);
s1 = null;
// 经过此番处理,基本的字符串值就变得跟对象一样了。
- 以上步骤也分别适用于Boolean Number类型对应的布尔值和数字值
- 引用类型与基本包装类型的主要区别就是对象的生存期。使用new操作符创建的引用类型的实例,在执行流离开当前作用域之前都一直保存在内存中。而自动创建的基本包装类型的对象,则只存在于一行代码的执行瞬间,然后立即销毁。这意味着我们不能再运行时为基本类型值添加属性和方法
var s1 = "some text";
// 这里创建的String对象在执行第三行代码时就已经被销毁了
s1.color = "red";
// 这里又创建了自己的String对象,而该对象没有color属性
console.log(s1.color); // undefined
- 可以显式的调用Boolean,Number,String来创建基本包装类型的对象。但应该在必要的时候这样做,因为很容易分不清实在处理基本类型还是引用类型的值。
- 对基本包装类型的实例调用typeof会返回"object",而且所有基本包装类型的对象都会被转为布尔值true
- Object构造函数也会像工厂方法一样,根据传入值的类型返回相应基本包装类型的实例。
var obj = new Object("some text");
console.log(obj instanceof String); // true
- 使用new调用基本包装类型的构造函数,与直接调用同名的转型函数是不一样的
// number中保存的是基本类型的值25
// obj中保存的是Number的实例(对象)
var value = "25";
var number = Number(value); // 转型函数
console.log(typeof number); // "number"
var obj = new Number(value); // 构造函数
console.log(typeof obj); // "object"
Boolean类型
- Boolean类型的实例重写了valueOf()方法,返回基本类型值true或false
- 重写了toString()方法,返回字符串"true" "false"
- 可是Boolean对象在ECMAScript中的用处不大,因为经常造成误解。其中最常见的问题就是在布尔表达式中使用Boolean对象
-
基本类型与引用类型的布尔值还有两个区别。
- typeof操作符对基本类型返回"boolean", 引用类型返回"object"
- 由于Boolean对象是Boolean类型的实例,所以使用instanceof操作符测试Boolean对象会返回true,而测试基本类型的布尔值则返回false
// 使用Boolean对象生成的实例,在布尔表达式中,等同于一个普通对象
// 所以result1 返回true
var falseObject = new Boolean(false);
var result1 = falseObject && true; // true
var falseValue = false;
var result2 = falseValue && true; // false
console.log(typeof falseOjbect); // object
console.log(typeof falseValue); // boolean
console.log(falseObject instanceof Boolean); // true
console.log(falseValue instanceof Boolean); // false
- 理解基本类型的布尔值与Boolean对象之间的区别非常重要——当然,我们建议是永远不要使用Boolean对象
Number类型
- toLocaleString() toString() 返回字符串形式的数值
- valueOf() 返回基本类型的数值
- toString() 传递一个表示基数的参数,告诉它返回几进制的字符串形式
var num = 10;
console.log(num.toString()); // "10"
console.log(num.toString(2)); // "1010"
console.log(num.toString(8)); // "12"
console.log(num.toString(10)); // "10"
console.log(num.toString(16)); // "a"
-
除了继承的方法,NUmber类型还提供了一些用于将数值格式化为字符串的方法。
-
toFixed() 按照指定的小数位返回数值的字符串表示。
- 超出位数的会进行摄入,舍入规则因浏览器而异,IE8及之前不能正确舍入的范围在{(-0.94,-0.5],[0.5,0.94)])},在这个范围内,IE会返回0,而不是-1或1,IE9修复了这个问题。
- 标准实现的规范,toFixed() 尅表示带有0到20个小数位的数值,有些浏览器可以实现更多
- toExponential() 返回以指数表示法(也称e表示法)表示的数值的字符串形式。同样接受一个参数,制定输出结果的小数位
var num = 10; console.log(num.toExponentail(1)); // "1.0e+1"
- toPrecision() 这个方法会返回一个合适的表达方式 返回固定大小(fixed)格式,也可能返回指数(exponential)格式
var num = 99; console.log(num.toPrecision(1)); // "1e+2" console.log(num.toPrecision(2)); // "99" console.log(num.toPrecision(3)); // "99.0"
-
- 同样,我们不建议直接实例化Number类型,原因与创建Boolean对象一样
var numberObject = new Number(10);
var numberValue = 10;
console.log(typeof numberObject); // "object"
console.log(typeof numberValue); // "number"
console.log(numberObject instanceof Number); // true
console.log(numberValue instanceof Number); // false
String类型
- 继承的方法 valueOf() toLocaleString() toString() 都返回字符串所表示你的基本字符串值
- String类型每个实例都有一个length属性,表示字符串中包含多少个字符。即使字符串中包含双字节字符串(不是占一个字节的ASCII字符),每个字符也仍然算一个字符。
var stringValue = "hello world";
console.log(stringValue.length); // "11"
字符方法
- 两个用于访问字符串中特定字符的方法是 charAt() charCodeAt() 都接受一个参数,基于0的字符位置。
- charAt() 方法以单字符字符串的形式返回给定位置的那个字符(ECMAScript中没有字符类型)
var stringValue = "hello world";
console.log(stringValue.charAt(1)); // "e"
- charCodeAt() 返回字符编码
var stringValue = "hello world";
console.log(stringValue.charCodeAt(1)); // "101" 小写e的字符编码
- ECMAscript还定义了方括号访问字符的方法 IE7及以前的版本会返回undefined值
var stringValue = "hello world";
console.log(stringValue[1]); // "e"
字符串的操作方法
- concat() 用于将一个或多个字符串拼接起来,返回拼接得到的新字符串,可以接受任意多个参数。虽然concat()实现了拼接方法,但实践中更多还是使用加号操作符(+),简单易行。
var stringValue = "hello ";
var result1 = stringValue.concat("world"); // "hello world"
var result2 = stringValue.concat("world", "!"); // "hello world!"
console.log(stringValue); // "hello
-
ECMAscript还提供了三个基于子字符串创建新字符串的方法 slice() substr() substring()
- 这个三个方法都会返回被操作符字符串的一个子字符串,而且也都接收一个或两个参数。第一个参数制定字符串的开始位置,第二个(可选)表示子字符串到哪里结束
- 三个方法都不会修改字符串本身的值——它们只是返回一个基本类型的字符串值,对于原始字符串没有任何影响
- slice() substring() 的第二个参数制定的是字符串最后一个字符后的位置
- substr() 的第二个参数制定的则是返回的字符个数
var stringValue = "hello world"; console.log(stringValue.slice(3)); // "lo world" console.log(stringValue.substring(3)); // "lo world" console.log(stringValue.substr(3)); // "lo world" // substr() 方法第二个参数制定是要返回的字符个数 // 而slice() substring() 指定的是结束位置 // "world"中的o处于位置7,因此结果中不包含o console.log(stringValue.slice(3, 7)); // "lo w" console.log(stringValue.substring(3, 7)); // "lo w" console.log(stringValue.substr(3, 7)); // "lo worl"
- 在参数是负值的情况下,它们的行为就不尽相同了
var stringValue = "hello world"; // slice() 会将传入的负值与字符串的长度相加 // substring() 会将传入的负值转换为0 // substr() 第一个参数如果是负值会加上字符串长度,第二个参数如果是负值,就会转换为0 console.log(stringValue.slice(-3)); // "rld" console.log(stringValue.substring(-3)); // "hello world" console.log(stringValue.substr(-3)); // "rld" console.log(stringValue.slice(3, -4)); // "lo w" // 这里实际上相当于substring(3, 0),而这个方法会将较小的参数作为开始位置 console.log(stringValue.substring(3, -4)); // "hel" console.log(stringValue.substr(3, -4)); // ""
字符串的位置方法
- indexOf() lastIndexOf() 从一个字符串搜索给定的字符串,然后返回字符串的位置(没有找到就返回-1)。lastIndexOf() 从末尾向前搜索
- 两个方法都可以接受第二个参数,表示从字符串中的哪个位置开始搜索。indexOf() 会忽略置顶位置之前的向后搜索,而lastIndexOf() 则相反
var stringValue = "hello world";
console.log(stringValue.indexOf("o")); // 4
console.log(stringValue.lastIndexOf("o")); // 7
console.log(stringValue.indexOf("o", 6)); // 7
console.log(stringValue.lastIndexOf("o", 6)); // 4
- 在使用第二个参数的情况下,可以通过循环调用indexOf()或lastIndexOf()来找到所有匹配的子字符串
var stringValue = "Lorem ipsum dolor sit amet, consectetur adipisicing elit";
var positions = new Array();
var pos = stringValue.indexOf("e");
while(pos > -1) {
positions.push(pos);
pos = stringValue.indexOf("e", pos + 1);
}
console.log(posisionts); // [3, 24, 32, 35, 52]
trim() 方法
- ECMAScript为所有字符串定义了trim()方法。创建一个字符串的副本,删除前置及后缀的所有空格,然后返回结果
- IE9+, Firefox3.5+, Safari5+, Opera10.5+, Chrome 8+ 还支持trimLeft() trimRight()
var str = " hello world "
str.trim(); // "hello world"
console.log(str); // " hello world "
字符串大小写转换方法
- toLowerCase(), toUpperCase() 是两个经典方法,借鉴自java.lang.String的同名方法
- toLocaleLowerCase(), toLocaleUpperCase() 方法则是针对特定地区的实现。部分地区(包括简体中文),和上面两个方法得到的值相同,但少数语言(如土耳其语)会为Unicode大小写转换应用特殊的规则,这时就必须使用针对地区的方法来保证实现的正确的转换
var stringValue = "hello world";
console.log(stringValue.toLocaleUpperCase()); // "HELLO WORLD"
console.log(stringValue.toUpperCase()); // "HELLO WORLD"
console.log(stringValue.toLocaleLowerCase()); // "hello world"
console.log(stringValue.toLowerCase()); // "hello world"
字符串的模式匹配方法
- match() 在字符串上调用这个方法,本质上与调用RegExp的exec()方法相同。只接收一个参数,要么是一个正则表达式,要么是一个RegExp对象
var txt = "cat, bat, sat, fat";
var pattern = /.at/;
// 与pattern.exec()相同
var matches = txt.match(pattern);
console.log(matches.index); // 0
console.log(matches[0]); // "cat"
console.log(matches.lastIndex); // 0
- 另一个用于查找模式的方法是search() 唯一的参数与match()相同,返回字符串中第一个匹配项的索引;如果没有返回-1
var txt = "cat, bat, sat, fat";
var pos = txt.search(/at/);
console.log(pos); // 1
-
为了简化替换子字符串的操作,ECMAscript提供了replace()方法,两个参数,一个是RegExp对象或者一个字符串(这个字符串不会被转成正则表达式),第二个参数可以是一个字符串或者一个函数。如果第一个参数是字符串,那么只会替换掉第一个子字符串。要想替换所有子字符串,唯一的办法就是提供一个正则表达式,而起要制定全局标识(g)
var txt = "cat, bat, sat, fat"; var result = txt.replace("at", "ond"); // "cound, bat, sat, fat" result = txt.replace(/at/g, "ond"); // "cond, bond, sond, fond"
- 第二个参数是字符串,还可以使用一些特殊子反复序列,将正则表达式操作得到的值插入到结果字符串中。
| 字符序列 | 替换文本 |
| -------- | ----------------------------------------------------------------------------------------------------------- |
| $$ | $ |
| $& | 匹配整个模式的子字符串。与RegExp.lastMatch的值相同 |
| $' | 匹配子字符串之前的字符串。与RegExp.leftContext的值相同 |
| $` | 匹配子字符串之后的字符串。与RegExp.rightContext的值相同 |
| $n | 匹配第n个捕获组的子字符串,其中n等于0~9。$0,$1...$9 。如果正则表达式中没有定义捕获组,则使用空字符串 |
| $nn | 匹配第nn个捕获组的子字符串,其中n等于01~99。$01,$02...$99 。如果正则表达式中没有定义捕获组,则使用空字符串 |
var txt = "cat, bat, sat, fat";
result = txt.replace(/(.at)/g, "word ($1)");
// word (cat), word (bat), word (sat), word (fat)
- 第二个参数是函数。在只有一个匹配项(即与模式匹配的字符串)的情况下,会向这个函数传递三个参数:模式的匹配项(定义了多个捕获组,这里就会有多个)、模式匹配项在字符串中的位置和原始字符串。
function htmlEscape(text) {
return text.replace(/[<>"&]/g, function(match, pos, orginalText) {
switch (match) {
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case "\"":
return """;
}
});
}
console.log(htmlEscape("<p class=\"greeting\">Hello world!</p>"))
// <p class="greeting">Hello world!</p>
- split() 方法可以基于指定的分隔符将一个字符串分割成多个子字符串,并将结果放在一个数组中。分隔符可以是字符串,也可以是一个RegExp对象(这个方法不会将字符串看成正则表达式)。split() 方法可以接受可选的第二个参数,用于指定数组的大小,以便确保返回的数组不会超过既定大小。
var colorText = "red,blue,green,yellow";
var colors1 = colorText.split(","); // ["red", "blue", "green", "yellow"]
var colors2 = colorText.split(",", 2); // ["red", "blue"]
// 需要注意的是,返回结果第一项和最后一项是空字符串
// /[^\,]+/ 表示匹配逗号之前的单词 不包括逗号 匹配项就是
// "red" "blue" “green" "yellow"
// "red"之前和“yellow"之后没有字符,所以第一项和最后一项是空字符串
var colors3 = colorText.split(/[^\,]+/); // ["", ",", ",", ",", ""]
localeCompare() 方法
-
这个方法比较两个字符串,并返回下列值中的一个:
- 如果字符串在字母表中应该排在字符串参数之前,则返回一个负数,(大多数情况下是-1,具体的值要视实现而定)
- 如果字符串等于字符串参数,则返回0
- 如果字符串在字母表中应该排在字符串参数之后,则返回一个正数(大多数情况下是1, 具体的值同样要视实现而定)。
var stringValue = "yellow";
console.log(stringValue.localeCompare("brick")); // 1
console.log(stringValue.localeCompare("yellow")); // 0
console.log(stringValue.localeCompare("zoo")); // -1
- 再次强调,因为localeCompared() 返回的数值取决于实现,所以最好是像下面所示
function determineOrder(value) {
var result = stringValue.localeCompare(value);
if (result < 0) {
console.log("The string 'yellow' comes before the string '" + value + "'.");
} else if (result > 0) {
console.log("The string 'yellow' comes after the string '" + value + "'.");
} else {
console.log("The string 'yellow' is equal to the string '" + value + "'.");
}
}
determineOrder("brick");
determineOrder("yellow");
determineOrder("zoo");
fromCharCode() 方法
- 这个方法的任务是接收一或多个字符编码,然后将它们转换成一个字符串
console.log(String.fromCharCode(104, 101, 108, 108, 111)); // "hello"
HTML 方法
- 早期的web浏览器实现了一些专门用于简化HTML格式化任务的方法,不建议使用
单体内置对象
- ECMA-262对内置对象的定义:“由ECMAScript实现提供的、不依赖于宿主环境的对象,这些对象在ECMAScript程序执行之前就已经存在了。”意思是开发者不必显式的实例化内置对象,因为它们已经实例化了。
Global 对象
- ECMAScript中的Global对象在某种意义上是作为一个终极的“兜底儿对象”。不属于任何其他对象的属性和方法,最终都是它的属性和方法。
- 所有在全局作用域中定义的属性和函数都是Global对象的属性。
- 诸如 isNaN(), isFinite(), parseInt(), parseFloat() 实际上全都是Global对象的方法。除此之外,Global还包含其他一些方法。
URI编码方法
- Gloabal对象的encodeURI() 和 encodeURIComponent() 方法可以对URI(Uniform Resource Identifiers, 通用资源标识符)进行编码,以便发送给浏览器。有效的URI中不能包含某些字符,例如空格。而这两个URI编码方法就可以对URI进行编码,它们特殊的UTF-8编码替换所有无效的字符,从而让浏览器能够接受和理解。
- encodeURI() 主要用于整个URI(例如 http://www.wrox.com/illegal value.htm) 不会对本身属性URI的特殊字符进行编码,例如冒号、正斜杠、问好和井字号
- encodeURIComponent() 主要用于对URI中的某一段(例如前面URI中的illegal value.htm)进行编码。会对发现的任何非标准字符进行编码。
var uri = "http://www.wrox.com/illegal value.htm#start";
encodeURI(rui); // "http://www.wrox.com/illegal%20value.htm#start"
encodeURIComponent(rui); // "http%3A%2F%2Fwww.wrox.com%2Fillegal%20value.htm%23start"
// encodeURI 只对空格编码
// encodeURIComponents 所有非字母数字字符
- 与encodeURI() 和 encodeURIComponent() 方法对应的是 decodeURI() decodeURIComponent()
- dencodeURI() 只能对encodeURI()替换的字符进行编码。%20替换成一个空格,但不会对%23(#)作处理
- decodeURIComponent() 能解码所有字符
eval() 方法
- 最后一个,大概也是ECMAScript中最强大的方法。它只接受一个参数,即,要执行的的ECMAScript(或 JavaScript)字符串
- eval() 中创建的任何变量或函数都不会被提升,因为在解析代码的时候,它们被包含在一个字符串中,只在eval() 执行的时候创建
eval("var msg = 'hello world';");
console.log(msg); // hello world
eval("function sayHi() { alert('hi'); }");
sayHi();
- 严格模式下,在外部访问不到eval()中创建的任何变量或函数,因此前面两个例子都会导致错误。
- 在严格模式下,为eval赋值也会导致错误
"use strict"
eval = "hi"; // causes error
- 使用eval()时必须极为谨慎,特别实在用它执行用户输入数据的情况下。否则可能会有恶意用户输入威胁你的站点或应用程序安全的代码(即所谓的代码注入)
Global对象的属性
- 有些属性之前介绍过,诸如 undefined, NaN, Infinity 都是Global对象的属性。此外所有原生引用类型的构造函数,像Object Function也都是Global对象的属性
属性 | 说明 |
---|---|
undefined | 特殊值undefined |
NaN | 特殊值NaN |
Infinity | 特殊值Infinity |
object | 构造函数object |
Array | 构造函数Array |
Function | 构造函数Function |
Boolean | 构造函数Boolean |
String | 构造函数String |
Number | 构造函数Number |
Date | 构造函数Date |
RegExp | 构造函数RegExp |
Error | 构造函数Error |
EvalError | 构造函数RegExp |
RangeError | 构造函数RangeError |
ReferenceError | 构造函数ReferenceError |
SyntaxError | 构造函数SyntaxError |
TypeError | 构造函数TypeError |
URIError | 构造函数URIError |
- ECMAScript 5 明确进制给undefined, NaN 和 Infinity 赋值, 这样做即使在非严格模式下也会导致错误。
window对象
- ECMAScript 虽然没有指出如何直接访问global对象,但Web浏览器都是将这个全局对象作为window对象的一部分加以实现。因此全局作用域中声明的所有变量和函数,就都成为了window对象的属性
- 另一种取得Global对象的方法是使用this
var global = function() {
return this;
}();
Math 对象
- 与JavaScript直接编写的计算功能相比,Math对象提供的计算功能执行起来要快的多。
- Math对象的属性
属性 | 说明 |
---|---|
Math.E | 自然对数的底数,即常量e的值 |
Math.LN10 | 10的自然对数 |
Math.LN2 | 2的自然对数 |
Math.LOG2E | 以2为底e的对数 |
Math.LOG10E | 以10为底e的对数 |
Math.PI | π的值 |
Math.SQRT1_2 | 1/2的平方根 |
Math.SQRT2 | 2的平方根 |
-
min() max() 方法用于确定一组数值中的最小值和最大值。可以接收任意个数值参数。
var max = Math.max(3, 54, 32, 16); // 54 var min = Math.min(3, 54, 32, 16); // 3
- 找到数组中的最大或最小值,可以像下面这样使用
- 这个技巧的关键是把Math作为apply()的第一个参数,从而正确的设置this值,然后可以将任何数组作为第二个参数(作为arguments对象)
var values = [1, 2, 3, 5, 6, 7, 8]; // 这个写法相当于 把数组的项作为一个个数值参数传给函数 // Math.max(1, 2, 3, 5, 6, 7, 8) var max = Math.max.apply(Math, values); var min = Math.min.apply(Math, values);
-
舍入方法 将小数值舍入为整数的几个方法:Math.ceil(), Math.floor(), Math.round()
- Math.ceil() 执行向上舍入,即它总是将数值向上舍入为最接近的整数
- Math.floor() 执行向下舍入,即它总是将数值向下舍入为最接近的整数
- Math.round() 执行标准舍入,即它总是将数值四舍五入为最接近的整数
- random() 返回大于等于0小于1的一个随机数
// 随机生成一个1到10之间的数值
var num = Math.floor(Math.random() * 10 + 1);
function selectFrom(lowerValue, upperValue) {
var choices = upperValue - lowerValue + 1;
return Math.floor(Math.random() * choices + lowerValue);
}
var num = selectFrom(2, 10); // 介于2和10之间(包括2和10)的一个数值
// 利用这个函数可以方便的从数组中随机取出一项
var colors = ["red", "green", "blue", "yellow", "black", "purple", "brown"]
var color = colors[selectFrom(0, colors.length - 1)];
其他方法
方法 | 说明 |
---|---|
Math.abs(num) | 返回num的绝对值 |
Math.exp(num) | 返回Math.E的num次幂 |
Math.log(num) | 返回num的自然对数 |
Math.pow(num, power) | 返回num的power次幂 |
Math.sqrt(num) | 返回num的平方根 |
Math.acos(x) | 返回x的反余弦值 |
Math.asin(x) | 返回x的反正弦值 |
Math.atan(x) | 返回x的反正切值 |
Math.atan2(y,x) | 返回y/x的反正切值 |
Math.cos(x) | 返回x的余弦值 |
Math.sin(x) | 返回x的正弦值 |
Math.tan(x) | 返回x的正切值 |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。